/******************************************************************************
 *                                                                            *
 *              (c) Copyright 2003 Laszlo Szathmary                           *
 *                                                                            *
 * This program is free software; you can redistribute it and/or modify it    *
 * under the terms of the GNU Lesser General Public License as published by   *
 * the Free Software Foundation; either version 2.1 of the License, or        *
 * (at your option) any later version.                                        *
 *                                                                            *
 * This program is distributed in the hope that it will be useful, but        *
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public    *
 * License for more details.                                                  *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program; if not, write to the Free Software Foundation,    *
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                              *
 *                                                                            *
 ******************************************************************************/

package dot;

import java.io.*;

/**
 * <dl>
 * <dt>Purpose: GraphViz Java API
 * <dd>
 * <dt>Description:
 * <dd>With this Java class you can simply call dot from your Java programs
 * <dt>Example usage:
 * <dd>
 * 
 * <pre>
 * GraphViz gv = new GraphViz();
 * gv.addln(gv.start_graph());
 * gv.addln(&quot;A -&gt; B;&quot;);
 * gv.addln(&quot;A -&gt; C;&quot;);
 * gv.addln(gv.end_graph());
 * System.out.println(gv.getDotSource());
 * 
 * File out = new File(&quot;out.gif&quot;);
 * gv.writeGraphToFile(gv.getGraph(gv.getDotSource()), out);
 * </pre>
 * 
 * </dd>
 * </dl>
 * 
 * @version v0.1, 2003/12/04 (Decembre)
 * @author Laszlo Szathmary (<a
 *         href="szathml@delfin.unideb.hu">szathml@delfin.unideb.hu</a>)
 */
public class GraphViz {
  /**
   * The dir where temporary files will be created.
   */
  private static String TEMP_DIR = "/tmp";

  /**
   * Where is your dot program located? It will be called externally.
   */
  private static String DOT = "/usr/bin/dot";

  /**
   * The source of the graph written in dot language.
   */
  private StringBuffer graph = new StringBuffer();

  /**
   * Constructor: creates a new GraphViz object that will contain a graph.
   */
  public GraphViz() {
  }

  /**
   * Returns the graph's source description in dot language.
   * 
   * @return Source of the graph in dot language.
   */
  public String getDotSource() {
    return graph.toString();
  }

  /**
   * Adds a string to the graph's source (without newline).
   */
  public void add(String line) {
    graph.append(line);
  }

  /**
   * Adds a string to the graph's source (with newline).
   */
  public void addln(String line) {
    graph.append(line + "\n");
  }

  /**
   * Adds a newline to the graph's source.
   */
  public void addln() {
    graph.append('\n');
  }

  /**
   * Returns the graph as an image in binary format.
   * 
   * @param dot_source Source of the graph to be drawn.
   * @return A byte array containing the image of the graph.
   */
  public byte[] getGraph(String dot_source) throws java.io.IOException {
    File dot;
    byte[] img_stream = null;

    // try {
    dot = writeDotSourceToFile(dot_source);
    if (dot != null) {
      img_stream = get_img_stream(dot);
      if (dot.delete() == false)
        System.err.println("Warning: " + dot.getAbsolutePath() + " could not be deleted!");
      return img_stream;
    }
    return null;
    // } catch (java.io.IOException ioe) {
    // return null;
    // }
  }

  /**
   * Writes the graph's image in a file.
   * 
   * @param img A byte array containing the image of the graph.
   * @param file Name of the file to where we want to write.
   * @return Success: 1, Failure: -1
   */
  public void writeGraphToFile(byte[] img, String file) throws java.io.IOException {
    File to = new File(file);
    writeGraphToFile(img, to);
  }

  /**
   * Writes the graph's image in a file.
   * 
   * @param img A byte array containing the image of the graph.
   * @param to A File object to where we want to write.
   * @return Success: 1, Failure: -1
   */
  public void writeGraphToFile(byte[] img, File to) throws java.io.IOException {
    // try {
    FileOutputStream fos = new FileOutputStream(to);
    fos.write(img);
    fos.close();
    // } catch (java.io.IOException ioe) { return -1; }
    // return 1;
  }

  /**
   * It will call the external dot program, and return the image in binary
   * format.
   * 
   * @param dot Source of the graph (in dot language).
   * @return The image of the graph in .gif format.
   */
  private byte[] get_img_stream(File dot) {
    File img;
    byte[] img_stream = null;

    try {
      img = File.createTempFile("graph_", ".gif", new File(TEMP_DIR));
      String temp = img.getAbsolutePath();

      Runtime rt = Runtime.getRuntime();
      String cmd = DOT + " -Tgif " + dot.getAbsolutePath() + " -o" + temp;
      Process p = rt.exec(cmd);
      p.waitFor();

      FileInputStream in = new FileInputStream(temp);
      img_stream = new byte[in.available()];
      in.read(img_stream);
      // Close it if we need to
      if (in != null)
        in.close();

      if (img.delete() == false)
        System.err.println("Warning: " + temp + " could not be deleted!");
    } catch (java.io.IOException ioe) {
      System.err.println("Error:    in I/O processing of tempfile in dir " + TEMP_DIR + "\n");
      System.err.println("       or in calling external command");
      ioe.printStackTrace();
    } catch (java.lang.InterruptedException ie) {
      System.err.println("Error: the execution of the external program was interrupted");
      ie.printStackTrace();
    }

    return img_stream;
  }

  /**
   * Writes the source of the graph in a file, and returns the written file as a
   * File object.
   * 
   * @param str Source of the graph (in dot language).
   * @return The file (as a File object) that contains the source of the graph.
   */
  private File writeDotSourceToFile(String str) throws java.io.IOException {
    File temp;
    try {
      temp = File.createTempFile("graph_", ".dot.tmp", new File(TEMP_DIR));
      FileWriter fout = new FileWriter(temp);
      fout.write(str);
      fout.close();
    } catch (Exception e) {
      System.err.println("Error: I/O error while writing the dot source to temp file!");
      return null;
    }
    return temp;
  }

  /**
   * Returns a string that is used to start a graph.
   * 
   * @return A string to open a graph.
   */
  public String start_graph() {
    return "digraph G {";
  }

  /**
   * Returns a string that is used to end a graph.
   * 
   * @return A string to close a graph.
   */
  public String end_graph() {
    return "}";
  }
}
